//
//  NBAVTool.m
//  音视频合成demo
//
//  Created by VictorLiu on 2018/10/19.
//  Copyright © 2018 VictorLiu. All rights reserved.
//

#import "NBAVTool.h"
#import <AVFoundation/AVFoundation.h>
#import <AVKit/AVPlayerViewController.h>

@interface NBAVTool ()

/** 录音对象 */
@property (nonatomic, strong) AVAudioRecorder *recorder;

/** 开启音频录制 回调 */
@property (nonatomic, strong) void(^startRecordSuccessBlock)(BOOL success, NSString *errorStr);

/** 正在处理的音频 */
@property (nonatomic, strong) NBRecordAudioModel *currentAudio;

/** 开始录制的时间戳 */
@property (nonatomic, assign) NSTimeInterval startRecordTimeInterval;

@property (nonatomic, strong) void(^audioBlock)(BOOL success, NSString *errorStr, NBRecordAudioModel *audio);

/** 音频播放对象 */
@property (nonatomic, strong) AVAudioPlayer *audioPlayer;


/** 视频播放控制器 */
@property (nonatomic,strong) AVPlayerViewController *moviePlayerView;

@property (nonatomic, strong) void(^videoBlock)(BOOL success, NSString *errorStr, NBVideoModel *video);

@end

@implementation NBAVTool

#pragma mark --- 对外
+ (void)startRecordAudio:(void(^)(BOOL success, NSString *errorStr))successBlock {
    NBAVTool *tool = [self sharedTool];
    tool.startRecordSuccessBlock = successBlock;
    [tool startRecord];
}

+ (void)stopRecordAudio:(void(^)(BOOL success, NSString *errorStr, NBRecordAudioModel *audio))audioBlock {
    NBAVTool *tool = [self sharedTool];
    tool.audioBlock = audioBlock;
    [tool stopRecord];
}

+ (void)deleteLocalRecordAudio:(NSString *)path finish:(void(^)(BOOL success, NSString *errorStr))finishBlock {
    
    NBAVTool *tool = [self sharedTool];
    [tool deleteFile:path withCompletionHandle:finishBlock];
}

+ (void)mergeAudio:(NSArray *)pathArray completionHandle:(void(^)(BOOL success, NSString *errorStr, NBRecordAudioModel *audio))audioBlock {
    NBAVTool *tool = [self sharedTool];
    tool.audioBlock = audioBlock;
    [tool mergeAudios:pathArray];
}

+ (void)playAudioPath:(NSString *)path withCompletionHandle:(void(^)(BOOL success, NSString *errorStr))completionHandle {
    NBAVTool *tool = [self sharedTool];
    [tool playAudio:path withBlock:completionHandle];
}

/** 播放视频 */
+ (void)playVideoPath:(NSString *)path withCompletionHandle:(void(^)(BOOL success, NSString *errorStr))completionHandle {
    NBAVTool *tool = [self sharedTool];
    [tool playVideo:path withBlock:completionHandle];
}

+ (void)mergeAudio:(NSString *)audioPath andVideo:(NSString *)videoPath completionHandle:(void(^)(BOOL success, NSString *errorStr, NBVideoModel *video))videoBlock {
    NBAVTool *tool = [self sharedTool];
    tool.videoBlock = videoBlock;
    [tool mergeVideo:videoPath withAudio:audioPath];
}

#pragma mark --- 实例
+ (instancetype)sharedTool {
    
    static NBAVTool *tool = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        tool = [[NBAVTool alloc] init];
    });
    return tool;
}

- (NBRecordAudioModel *)createAudio {
    
    NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *fileName = [NSString stringWithFormat:@"/%.0lf.m4a", [[NSDate date] timeIntervalSince1970]];
    NSString *filePath = [path stringByAppendingString:fileName];
    NBRecordAudioModel *audioModel = [[NBRecordAudioModel alloc] init];
    audioModel.path = filePath;
    audioModel.fileName = fileName;
    return audioModel;
}

- (AVPlayerViewController *)moviePlayerView {
    if (!_moviePlayerView) {
        _moviePlayerView = [[AVPlayerViewController alloc] init];
        _moviePlayerView.videoGravity = AVLayerVideoGravityResizeAspect;
    }
    return _moviePlayerView;
}

- (NBVideoModel *)createVideo {
    
    NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *fileName = [NSString stringWithFormat:@"%.0lf.mp4", [[NSDate date] timeIntervalSince1970]];
    NSString *filePath = [NSString stringWithFormat:@"%@/%@", path, fileName];
    NBVideoModel *videoModel = [[NBVideoModel alloc] init];
    videoModel.path = filePath;
    videoModel.fileName = fileName;
    return videoModel;
}

#pragma mark --- 音频操作
/** 开始录音 */
- (void)startRecord {
    
    if (self.recorder && [self.recorder isRecording]) {
        if (self.startRecordSuccessBlock) {
            self.startRecordSuccessBlock(NO, @"正在录制中");
        }
        return;
    }
    
    AVAudioSession *session =[AVAudioSession sharedInstance];
    NSError *sessionError;
    [session setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];
    
    if (session == nil) {
        
        NSLog(@"Error creating session: %@",[sessionError description]);
        
    }else{
        [session setActive:YES error:nil];
        
    }
    
    self.currentAudio = [self createAudio];
    
    NSMutableDictionary *recordSettings = [[NSMutableDictionary alloc] init];
    [recordSettings setValue :[NSNumber numberWithInt:kAudioFormatLinearPCM] forKey: AVFormatIDKey];
    [recordSettings setValue :[NSNumber numberWithFloat:8000] forKey: AVSampleRateKey];
    [recordSettings setValue :[NSNumber numberWithInt:2] forKey: AVNumberOfChannelsKey];
    [recordSettings setValue:[NSNumber numberWithInt:AVAudioQualityMax] forKey:AVEncoderAudioQualityKey];
    [recordSettings setValue:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];
    [recordSettings setValue:[NSNumber numberWithInt:kAudioFormatMPEG4AAC] forKey:AVFormatIDKey];
    NSError *recorderError = nil;
    self.recorder = [[AVAudioRecorder alloc] initWithURL:[NSURL URLWithString:self.currentAudio.path] settings:recordSettings error:&recorderError];
    if (!self.recorder) {
        if (self.startRecordSuccessBlock) {
            self.startRecordSuccessBlock(NO, recorderError.description);
        }
        return;
    } else {
//        self.recorder.meteringEnabled = YES;
        [self.recorder prepareToRecord];
        [self.recorder record];
        self.startRecordTimeInterval = [[NSDate date] timeIntervalSince1970];
        if (self.startRecordSuccessBlock) {
            self.startRecordSuccessBlock(YES, nil);
        }
    }
}

/** 结束录音 */
- (void)stopRecord {
    
    if (self.recorder) {
        if ([self.recorder isRecording]) {
            NSTimeInterval duration = self.recorder.currentTime;
            [self.recorder stop];
//            NSTimeInterval duration = [[NSDate date] timeIntervalSince1970] - self.startRecordTimeInterval;
            
            if (duration - 3 > -0.1) {
                self.currentAudio.duration = duration;
                if (self.audioBlock) {
                    self.audioBlock(YES, nil, self.currentAudio);
                }
            } else {
                if (self.audioBlock) {
                    self.audioBlock(NO, @"录制时间太短，请重新开始", nil);
                }
            }
            
        } else {
            if (self.audioBlock) {
                self.audioBlock(NO, @"尚未开始录制", nil);
            }
        }
    } else {
        if (self.audioBlock) {
            self.audioBlock(NO, @"尚未开始录制", nil);
        }
    }
    
}

/** 合成音频 */
- (void)mergeAudios:(NSArray *)pathArray {
    
    if (!pathArray || ![pathArray isKindOfClass:[NSArray class]]) {
        if (self.audioBlock) {
            self.audioBlock(NO, @"参数错误", nil);
        }
        return;
    } else if (pathArray.count == 0 || pathArray.count == 1) {
        
        if (self.audioBlock) {
            self.audioBlock(NO, @"至少传入两个音频", nil);
        }
        return;
    }
    
    AVMutableComposition *mixComposition = [AVMutableComposition composition];
    AVMutableCompositionTrack *compositionAudioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    
    CMTime beginTime = kCMTimeZero;
    NSError *error = nil;
    
    for (int i = 0; i < pathArray.count; i++) {
        NSString *path = pathArray[i];
        AVURLAsset *audioAsset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:path] options:nil];
        CMTimeRange audio_timeRange = CMTimeRangeMake(kCMTimeZero, audioAsset.duration);
        NSArray *trackArray = [audioAsset tracksWithMediaType:AVMediaTypeAudio];
        AVAssetTrack *assetTrack = trackArray.firstObject;
        
        // 合成
        BOOL success = [compositionAudioTrack insertTimeRange:audio_timeRange ofTrack:assetTrack atTime:beginTime error:&error];
        if (!success) {
            if (self.audioBlock) {
                self.audioBlock(NO, [NSString stringWithFormat:@"插入第%d个音频失败", i + 1], nil);
            }
            return;
        }
        
        beginTime = CMTimeAdd(beginTime, audioAsset.duration);
    }
    
    // 导出
    AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetAppleM4A];
    NBRecordAudioModel *audioModel = [self createAudio];
    audioModel.duration = CMTimeGetSeconds(mixComposition.duration);
    exportSession.outputURL = [NSURL fileURLWithPath:audioModel.path];
    exportSession.outputFileType = AVFileTypeAppleM4A;
    __weak typeof(self) weakSelf = self;
    [exportSession exportAsynchronouslyWithCompletionHandler:^{
        dispatch_async(dispatch_get_main_queue(), ^{
            switch (exportSession.status) {
                case AVAssetExportSessionStatusCompleted: {
                    if (weakSelf.audioBlock) {
                        weakSelf.audioBlock(YES, nil, audioModel);
                    }
                    break;
                }
                case AVAssetExportSessionStatusFailed: {
                    if (weakSelf.audioBlock) {
                        weakSelf.audioBlock(NO, @"导出合成文件失败", nil);
                    }
                    break;
                }
                case AVAssetExportSessionStatusCancelled: {
                    if (weakSelf.audioBlock) {
                        weakSelf.audioBlock(NO, @"取消导出合成文件", nil);
                    }
                    break;
                }
                default:
                    break;
            }
        });
        
    }];
}

/** 播放音频 */
- (void)playAudio:(NSString *)path withBlock:(void(^)(BOOL success, NSString *errorStr))successBlock {
    AVAudioSession *session = [AVAudioSession sharedInstance];
    NSError *sessionError;
    [session setCategory:AVAudioSessionCategoryPlayback error:&sessionError];
    [session setActive:YES error:nil];
    if (sessionError) {
        if (successBlock) {
            successBlock(NO, @"设置扬声器失败");
        }
        return;
    }
    
    NSError *playerError = nil;
    self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL URLWithString:path] error:&playerError];
    if (playerError) {
        if (successBlock) {
            successBlock(NO, @"播放器设置失败");
        }
        return;
    }
    
    [self.audioPlayer prepareToPlay];
    [self.audioPlayer play];
}


#pragma mark --- 视频操作
/** 播放视频 */
- (void)playVideo:(NSString *)path withBlock:(void(^)(BOOL success, NSString *errorStr))successBlock {
    AVAudioSession *session = [AVAudioSession sharedInstance];
    NSError *sessionError;
    [session setCategory:AVAudioSessionCategoryPlayback error:&sessionError];
    [session setActive:YES error:nil];
    AVPlayer *player = [AVPlayer playerWithURL:[NSURL fileURLWithPath:path]];
    
    self.moviePlayerView.player = player;
    
    UIViewController *rootController = [UIApplication sharedApplication].keyWindow.rootViewController;
    UIViewController *currentController = nil;
    if ([rootController isKindOfClass:[UITabBarController class]]) {
        UITabBarController *tabbarControoler = (UITabBarController *)rootController;
        currentController = tabbarControoler.selectedViewController;
    } else {
        currentController = rootController;
    }
    __weak typeof(self) weakSelf = self;
    [currentController presentViewController:self.moviePlayerView animated:YES completion:^{
        [weakSelf.moviePlayerView.player play];
    }];
    successBlock(YES, nil);
}

/** 合成音视频 */
- (void)mergeVideo:(NSString *)videoPath withAudio:(NSString *)audioPath {
    if (!videoPath || videoPath.length == 0 || !audioPath || audioPath.length == 0) {
        if (self.videoBlock) {
            self.videoBlock(NO, @"参数错误", nil);
        }
        return;
    }
    
    AVMutableComposition *mixComposition = [AVMutableComposition composition];
    
    
    NSError *error = nil;
    
    // 获取视频track
    AVURLAsset *videoAsset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:videoPath] options:nil];
    
    NSArray *videoTrackArray = [videoAsset tracksWithMediaType:AVMediaTypeVideo];
    AVAssetTrack *videoAssetTrack = videoTrackArray.firstObject;
    CMTimeRange video_timeRange = CMTimeRangeMake(kCMTimeZero, videoAssetTrack.timeRange.duration);
    AVMutableCompositionTrack *videoCompositionTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
    BOOL videoSuccess = [videoCompositionTrack insertTimeRange:video_timeRange ofTrack:videoAssetTrack atTime:kCMTimeZero error:&error];
    if (!videoSuccess) {
        if (self.videoBlock) {
            self.videoBlock(NO, @"插入视频错误", nil);
        }
        return;
    }
    
    // 获取音频track
    AVURLAsset *audioAsset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:audioPath] options:nil];
    CMTimeRange audio_timeRange = CMTimeRangeMake(kCMTimeZero, audioAsset.duration);
    NSArray *audioTrackArray = [audioAsset tracksWithMediaType:AVMediaTypeAudio];
    AVAssetTrack *audioAssetTrack = audioTrackArray.firstObject;
    AVMutableCompositionTrack *audioCompositionTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    BOOL audioSuccess = [audioCompositionTrack insertTimeRange:audio_timeRange ofTrack:audioAssetTrack atTime:kCMTimeZero error:&error];
    if (!audioSuccess) {
        if (self.videoBlock) {
            self.videoBlock(NO, @"插入音频错误", nil);
        }
        return;
    }
    
    // 导出
    AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetMediumQuality];
    NBVideoModel *videoModel = [self createVideo];
//    videoModel.duration = CMTimeGetSeconds(mixComposition.duration);
    exportSession.outputURL = [NSURL fileURLWithPath:videoModel.path];
    exportSession.outputFileType = AVFileTypeAppleM4V;
    exportSession.shouldOptimizeForNetworkUse = YES;
    
    __weak typeof(self) weakSelf = self;
    [exportSession exportAsynchronouslyWithCompletionHandler:^{
        dispatch_async(dispatch_get_main_queue(), ^{
            switch (exportSession.status) {
                case AVAssetExportSessionStatusCompleted: {
                    if (weakSelf.videoBlock) {
                        weakSelf.videoBlock(YES, nil, videoModel);
                    }
                    break;
                }
                case AVAssetExportSessionStatusFailed: {
                    if (weakSelf.videoBlock) {
                        weakSelf.videoBlock(NO, @"导出合成文件失败", nil);
                    }
                    break;
                }
                case AVAssetExportSessionStatusCancelled: {
                    if (weakSelf.videoBlock) {
                        weakSelf.videoBlock(NO, @"取消导出合成文件", nil);
                    }
                    break;
                }
                default:
                    break;
            }
        });
        
    }];
}

- (AVAudioMix *)buildAudioMixWithVideoTrack:(AVCompositionTrack *)videoTrack VideoVolume:(float)videoVolume BGMTrack:(AVCompositionTrack *)BGMTrack BGMVolume:(float)BGMVolume controlVolumeRange:(CMTime)volumeRange {
    // 创建音频混合类
    AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
    
    // 拿到视频声音轨道设置音量
//    AVMutableAudioMixInputParameters *Videoparameters = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:videoTrack];
//    [Videoparameters setVolume:videoVolume atTime:volumeRange];
    
    // 设置背景音乐音量
    AVMutableAudioMixInputParameters *BGMparameters = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:BGMTrack];
    [BGMparameters setVolume:BGMVolume atTime:volumeRange];
    
    // 加入混合数组
    audioMix.inputParameters = @[BGMparameters];
    
    return audioMix;
}

#pragma mark --- 本地文件操作
- (void)deleteFile:(NSString *)path withCompletionHandle:(void(^)(BOOL success, NSString *errorStr))completionHandle {
    
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if ([fileManager fileExistsAtPath:path]) {
        NSError *error = nil;
        [fileManager removeItemAtPath:path error:&error];
        if (error) {
            if (completionHandle) {
                completionHandle(NO, @"文件删除失败");
            }
        } else {
            if (completionHandle) {
                completionHandle(YES, nil);
            }
        }
    } else {
        if (completionHandle) {
            completionHandle(NO, @"文件不存在，无法从删除");
        }
    }
    
}

@end
